#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
主题: 带额外状态信息的回调函数
问题: 你的代码中需要依赖到回调函数的使用(比如事件处理器、等待后台任务完成后的回调等)， 
    并且你还需要让回调函数拥有额外的状态值，以便在它的内部使用到。
提示 : 
"""

from functools import partial

def apply_async(func, args, *, callback):
    # Compute the result
    result = func(*args)

    # Invoke the callback with the result
    callback(result)

def print_result(result):
    print('Got:', result)

def add(x, y):
    return x + y 

class ResultHandler:
    '''类方案'''
    def __init__(self):
        self.sequence = 0

    def handler(self, result):
        self.sequence += 1
        output(self.sequence, result)

def make_handler():
    '''闭包方案'''
    sequence = 0
    def handler(result):
        nonlocal sequence   # 指示接下来的变量会在回调函数中被修改。如果没有这个声明，代码会报错
        sequence += 1
        output(sequence, result)
    return handler

def make_handler1():
    '''协程方案'''
    sequence = 0
    while True:
        result = yield
        sequence += 1
        output(sequence, result)

def output(sequence, result):
    print('[{}] Got: {}'.format(sequence, result))

def recipe1():
    apply_async(add, (2, 3), callback=print_result)
    apply_async(add, ('hello', 'world'), callback=print_result)

def recipe2():
    r = ResultHandler()

    apply_async(add, (2, 3), callback=r.handler)
    apply_async(add, ('hello', 'world'), callback=r.handler)

def recipe3():
    handler = make_handler()
    apply_async(add, (2, 3), callback=handler)
    apply_async(add, ('hello', 'world'), callback=handler)

def recipe4():
    handler = make_handler1()
    next(handler) # Advance to the yield
    apply_async(add, (2, 3), callback=handler.send)
    apply_async(add, ('hello', 'world'), callback=handler.send)    

def recipe5():
    seq = 1
    apply_async(add, (2, 3), callback=partial(output, seq)) 
    seq += 1
    apply_async(add, ('hello', 'world'), callback=partial(output, seq))  

    seq += 1
    apply_async(add, (2, 3), callback=lambda r: output(seq, r)) 
    seq += 1
    apply_async(add, ('hello', 'world'), callback=lambda r: output(seq, r))        

def main():
    print('recipe1'.center(20, '*'))
    recipe1()
    print('recipe2'.center(20, '*'))
    recipe2()
    print('recipe3'.center(20, '*'))
    recipe3()
    print('recipe4'.center(20, '*'))
    recipe4()
    print('recipe5'.center(20, '*'))
    recipe5()

if __name__ == '__main__':
    main()                  